package com.ym.medical.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.ym.core.redis.RedisKeyEnum;
import com.ym.core.redis.RedisKeyUtil;
import com.ym.core.redis.RedisUtil;
import com.ym.medical.service.ICommonSV;
import com.ym.medical.service.ISortTableSV;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 排序表
 *
 * @author: 李涛
 * @version: 2019年11月14日 16:35
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class SortTableSVImpl implements ISortTableSV {


	/**
	 * 拖拽标志对应的表和字段
	 */
	private static final Map<String, String> tables = new HashMap<>();

	/**
	 * 拖拽标志对应的表和字段
	 */
	private static final Map<String, String> rdsTables = new HashMap<>();

	/**
	 * 拖拽标志对应的表和字段
	 */
	private static final Map<String, String> tablesWhere = new HashMap<>();

	static {
		// 表名
		tables.put("app_banner", "sort_num");
		tables.put("app_health_plate", "sort_num");
		tables.put("app_start_page", "sort_num");
		tables.put("clinic_manual", "sort");
		tables.put("sys_menu", "menu_order");
		tables.put("nav_dept_adv", "sort_num");
		tables.put("project", "sort");
		tables.put("sys_dict", "item_order");

		//rds 表名
		rdsTables.put("news_information", "rds_sort");

		// 排序条件
		tablesWhere.put("sys_menu", "and parent_id = #{params.p0}");
		tablesWhere.put("clinic_manual", "and deleted !='01' ");
		tablesWhere.put("project", " and deleted !='01' ");
		tablesWhere.put("news_information", " and deleted !='01'");
		tablesWhere.put("sys_dict", "and dict_code = #{params.p0}");
	}

	@Autowired
	private ICommonSV commonSV;

	@Override
	public boolean sortTable(String tableName, Long top, Long mid, Long bottom, String[] whereCase) {
		String sort = tables.get(tableName);
		if (StringUtils.isBlank(sort)) {
			throw new UnsupportedOperationException();
		}
		Map<String, Object> params = new HashMap<>();
		//根据上下判定是上移还是下移
		Boolean down = null;
		Long midSort = null;
		Long topSort = null;
		Long bottomSort = null;
		if (top == null) {
			down = false;
		} else if (bottom == null) {
			down = true;
		} else {
			midSort = findSort(mid, tableName);
			topSort = findSort(top, tableName);
			bottomSort = findSort(bottom, tableName);
			down = midSort > topSort && midSort > bottomSort;
		}
		// 执行更新操作
		String updateSql = null;
		if (down) {
			if (topSort == null) {
				topSort = findSort(top, tableName);
			}
			List<Long> ids = findIds(top, mid, tableName, whereCase, "first");
			if (ids.isEmpty()) {
				return false;
			}
			updateSql = "update " + tableName + " set " + sort + " = " + sort + " + 1 where id in ( " + StringUtils.join(ids, ",") + " )";
			commonSV.update(updateSql, params);
			params.put("newSort", topSort);
			params.put("id", mid);
			updateSql = "update " + tableName + " set " + sort + " = #{params.newSort} where id = #{params.id} ";
			commonSV.update(updateSql, params);
		} else if (!down) {
			if (bottomSort == null) {
				bottomSort = findSort(bottom, tableName);
			}
			List<Long> ids = findIds(mid, bottom, tableName, whereCase, "last");
			if (ids.isEmpty()) {
				return false;
			}
			updateSql = "update " + tableName + " set " + sort + " = " + sort + " - 1 where id in ( " + StringUtils.join(ids, ",") + " )";
			commonSV.update(updateSql, params);
			params.put("newSort", bottomSort);
			params.put("id", mid);
			updateSql = "update " + tableName + " set " + sort + " = #{params.newSort} where id = #{params.id} ";
			commonSV.update(updateSql, params);
		}
		// 3.判断是否需要清除缓存，如字典
		if ("sys_dict".equals(tableName.trim())) {
			RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
			String delKey = RedisKeyUtil.KEY_PREFIX + ":SysDictCache::" + whereCase[0].trim() + ":Double:ALL";
			redisUtil.del(delKey);
			delKey = RedisKeyUtil.KEY_PREFIX + ":SysDictCache::" + whereCase[0].trim() + ":Double:List";
			redisUtil.del(delKey);
		}
		return true;
	}


	/**
	 * 查询两个ID之间的ID有哪些
	 *
	 * @param startId
	 * @param endId
	 * @param tableName
	 * @param whereCase
	 * @param removeTag
	 * @return
	 */
	private List<Long> findIds(Long startId, Long endId, String tableName, String[] whereCase, String removeTag) {
		String sort = tables.get(tableName);
		String sql = " select id " +
				" from " + tableName + " " +
				" where " + sort + " >= (select " + sort + " from " + tableName + " where id = #{params.startId}) " +
				" and " + sort + " <= (select " + sort + " from " + tableName + " where id = #{params.endId}) ";
		String whereCaseStr = tablesWhere.get(tableName);
		if (whereCaseStr != null) {
			sql += whereCaseStr;
		}
		sql += " order by  " + sort + " desc ";
		Map<String, Object> params = new HashMap<>();
		params.put("startId", startId);
		params.put("endId", endId);
		if (whereCase != null && whereCase.length > 0) {
			for (int i = 0; i < whereCase.length; i++) {
				params.put("p" + i, whereCase[i]);
			}
		}
		List<JSONObject> longs = commonSV.queryListJson(sql, params);
		if (StringUtils.isNotBlank(removeTag) && !longs.isEmpty()) {
			if ("last".equals(removeTag)) {
				longs.remove(longs.size() - 1);
			} else if ("first".equals(removeTag)) {
				longs.remove(0);
			}
		}
		return longs.stream().map(n -> n.getLong("id")).collect(Collectors.toList());
	}

	/**
	 * 根据ID查询序号
	 *
	 * @param id
	 * @param tableName
	 * @return
	 */
	private Long findSort(Long id, String tableName) {
		if (id == null) {
			return null;
		}
		String sql = " select " + tables.get(tableName) + " from " + tableName + " where id = #{params.id}";
		Map<String, Object> params = new HashMap<>();
		params.put("id", id);
		Number sortNum = commonSV.queryField(sql, params, Number.class);
		return sortNum.longValue();
	}

	@Override
	public void top(String tableName, Long id, String[] whereCase) {
		// 1.倒叙查询这个表最大的ID
		String sort = tables.get(tableName);
		String sql = " select id " +
				" from " + tableName + " where 1 = 1 ";
		String whereCaseStr = tablesWhere.get(tableName);
		if (whereCaseStr != null) {
			sql += whereCaseStr;
		}
		sql += " order by " + sort + " desc limit 1";
		Map<String, Object> params = new HashMap<>();
		if (whereCase != null && whereCase.length > 0) {
			for (int i = 0; i < whereCase.length; i++) {
				params.put("p" + i, whereCase[i]);
			}
		}
		List<JSONObject> longs = commonSV.queryListJson(sql, params);
		Long maxId = -1L;
		if (!longs.isEmpty()) {
			maxId = longs.get(0).getLong("id");
		}
		// 2.执行移动
		sortTable(tableName, null, id, maxId, whereCase);
	}

	@Override
	public void top(String tableName, Long[] ids, Date startTime, Date endTime) {

		//现在距离生效时间的有效期
		long effTime = startTime.getTime() - System.currentTimeMillis();

		//结束时间戳 （此处用作rds分数 后期根据此值判断下线时间）
		long endExpTime = endTime.getTime();

		RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);

		//置顶key:tableName
		String topKey = RedisKeyUtil.keyBuilder(RedisKeyEnum.SET_TOP_TABLE, tableName);

		//如果已经生效 直接加入缓存 否则让redis监听失效key
		if (effTime <= 0) {
			//更新数据库 置顶缓存排序字段
			topRdsSort(tableName, ids, null);
			redisUtil.zSet(topKey, StringUtils.join(ids, ","), endExpTime);
		} else {
			//置顶 topKey:score:value
			topKey = RedisKeyUtil.keyBuilder(RedisKeyEnum.SET_TOP_TABLE, tableName, String.valueOf(endExpTime), StringUtils.join(ids, ","));

			String effKey = RedisKeyUtil.keyBuilder(RedisKeyEnum.EFFECTIVE_VALUE, topKey);

			//指定key生效时间
			redisUtil.sSetAndTime(effKey, effTime / 1000, String.valueOf(effTime / 1000));
		}
	}

	@Override
	public void topRdsSort(String tableName, Long[] ids, String[] whereCase) {
		// 1.倒叙查询这个表最大的rdsSort
		String rdsSort = rdsTables.get(tableName);
		String sql = " select " + rdsSort +
				" from " + tableName + " where 1 = 1 ";
		String whereCaseStr = tablesWhere.get(tableName);
		if (whereCaseStr != null) {
			sql += whereCaseStr;
		}
		sql += " order by " + rdsSort + " desc limit 1";
		Map<String, Object> params = new HashMap<>();
		if (whereCase != null && whereCase.length > 0) {
			for (int i = 0; i < whereCase.length; i++) {
				params.put("p" + i, whereCase[i]);
			}
		}
		List<JSONObject> longs = commonSV.queryListJson(sql, params);
		Long maxSort = -1L;
		if (!longs.isEmpty()) {
			maxSort = longs.get(0).getLong(rdsSort);
		}
		// 2.置顶移动
		moveRdsSort(tableName, ids, maxSort, whereCase);
	}

	@Override
	public void restoreRdsSort(String tableName, String[] whereCase) {
		//1. 获取缓存中的id
		List<Long> longs = new ArrayList<>();
		RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
		String topKey = RedisKeyUtil.keyBuilder(RedisKeyEnum.SET_TOP_TABLE, tableName);

		Set<String> topIds = redisUtil.zGet(topKey);
		if (!topIds.isEmpty()) {
			Iterator<String> iterator = topIds.iterator();
			while (iterator.hasNext()) {
				String val = iterator.next();
				//获取分数 实际上就是时间戳
				double score = redisUtil.zScore(topKey, val);

				//与当时时间戳比较 如果大于或等于当前时间戳 删除value
				if (score <= System.currentTimeMillis()) {
					redisUtil.zSetRemove(topKey, val);
					continue;
				}
				Long[] ids = Convert.toLongArray(val);
				longs.addAll(Arrays.asList(ids));
			}
		}
		Long[] ids = Convert.toLongArray(longs);

		//2. 非置顶移动
		moveRdsSort(tableName, ids, null, whereCase);
	}

	/**
	 * 置顶取消
	 *
	 * @param tableName
	 * @param ids
	 */
	@Override
	public void topCancel(String tableName, Long[] ids) {
		String rdsSort = rdsTables.get(tableName);

		String where = null;
		//置顶取消
		String uptSql = " update " + tableName + " set " + rdsSort + " = 0 " + "where 1=1 ";
		if (ids != null && ids.length > 0) {
			where = "and id in (" + StringUtils.join(ids, ",") + ") ";
		}
		if (StringUtils.isNotBlank(where)) {
			uptSql += where;
		}

		String whereCaseStr = tablesWhere.get(tableName);
		if (whereCaseStr != null) {
			uptSql += whereCaseStr;
		}
		commonSV.update(uptSql, null);
	}

	/**
	 * 移动
	 *
	 * @param tableName
	 * @param ids
	 * @param maxSort
	 * @param whereCase
	 */
	private void moveRdsSort(String tableName, Long[] ids, Long maxSort, String[] whereCase) {
		String rdsSort = rdsTables.get(tableName);

		String uptSql = null;
		String where = null;
		if (maxSort != null) {
			// 置顶
			uptSql = " update " + tableName + " set " + rdsSort + " = " + maxSort + " + 1 " + "where 1=1 ";

			if (ids != null && ids.length > 0) {
				where = "and id in (" + StringUtils.join(ids, ",") + ") ";
			}
		} else {
			//非置顶
			uptSql = " update " + tableName + " set " + rdsSort + " = 0 " + "where 1=1 ";
			if (ids != null && ids.length > 0) {
				where = "and id not in (" + StringUtils.join(ids, ",") + ") ";
			}
		}

		if (StringUtils.isNotBlank(where)) {
			uptSql += where;
		}

		String whereCaseStr = tablesWhere.get(tableName);
		if (whereCaseStr != null) {
			uptSql += whereCaseStr;
		}
		Map<String, Object> params = new HashMap<>();
		if (whereCase != null && whereCase.length > 0) {
			for (int i = 0; i < whereCase.length; i++) {
				params.put("p" + i, whereCase[i]);
			}
		}
		commonSV.update(uptSql, params);
	}
}
